#include <srs_app_http_client.hpp>

#ifdef SRS_AUTO_HTTP_CORE

#include <utils/tools_curl.h>
#include <thread>

#ifndef WIN32
#include <arpa/inet.h>
#endif

using namespace std;

#include <srs_kernel_error.hpp>
#include <srs_kernel_log.hpp>
#include <srs_app_st.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_app_utility.hpp>
#include <srs_core_autofree.hpp>
#include <srs_app_http_conn.hpp>

SrsHttpClient::SrsHttpClient() {
#ifndef WIN32
    connected = false;
    stfd = NULL;
    skt = NULL;
    parser = NULL;
    timeout_us = 0;
#endif
}

SrsHttpClient::~SrsHttpClient() {
#ifndef WIN32
    disconnect();
    srs_freep(parser);
#endif
}

int SrsHttpClient::initialize(const string& h, int p, int64_t t_us) {
    int ret = ERROR_SUCCESS;

#ifndef WIN32
    srs_freep(parser);
    parser = new SrsHttpParser();
    if ((ret = parser->initialize(HTTP_RESPONSE)) != ERROR_SUCCESS) {
        srs_error("initialize parser failed. ret=%d", ret);
        return ret;
    }
    timeout_us = t_us;
#endif

    host = h;
    port = p;

    return ret;
}

int SrsHttpClient::post(const string& path, const string& req, ISrsHttpMessage** ppmsg) {
    *ppmsg = NULL;
    int ret = ERROR_SUCCESS;

#ifndef WIN32
    if ((ret = connect()) != ERROR_SUCCESS) {
        srs_warn("http connect server failed. ret=%d", ret);
        return ret;
    }

    // send POST request to uri
    // POST %s HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s
    std::stringstream ss;
    ss << "POST " << path << " "
       << "HTTP/1.1" << SRS_HTTP_CRLF
       << "Host: " << host << SRS_HTTP_CRLF
       << "Connection: Keep-Alive" << SRS_HTTP_CRLF
       << "Content-Length: " << std::dec << req.length() << SRS_HTTP_CRLF
       << "User-Agent: " << RTMP_SIG_SRS_NAME << RTMP_SIG_SRS_VERSION << SRS_HTTP_CRLF
       << "Content-Type: application/json" << SRS_HTTP_CRLF
       << SRS_HTTP_CRLF
       << req;

    std::string data = ss.str();

    if ((ret = skt->write((void*)data.c_str(), data.length(), NULL)) != ERROR_SUCCESS) {
        // disconnect when error.
        disconnect();
        srs_error("write http post failed. ret=%d", ret);
        return ret;
    }

    ISrsHttpMessage* msg = NULL;
    if ((ret = parser->parse_message(skt, NULL, &msg)) != ERROR_SUCCESS) {
        srs_error("parse http post response failed. ret=%d", ret);
        return ret;
    }
#else
    std::string resp;
    bool done = false;

    std::thread thd([this, &path, &done, &resp, &req] {
        resp = libcurlpost("http://" + host + ":" + std::to_string(port) + path, "", req);
        done = true;
    });
    thd.detach();

    while (done == false) {
        st_usleep(0);
    }

    auto msg = new SrsHttpMessage(nullptr, nullptr);
    if (resp.empty() == true) {
        msg->status_code(SRS_CONSTS_HTTP_InternalServerError);
    } else {
        msg->status_code(SRS_CONSTS_HTTP_OK);
        msg->body_write_all(resp);
    }
#endif
    if (msg != nullptr) {
        *ppmsg = msg;
    }
    srs_info("parse http post response success.");
    return ret;
}

int SrsHttpClient::get(const string& path, const string& req, ISrsHttpMessage** ppmsg) {
    *ppmsg = NULL;
    int ret = ERROR_SUCCESS;

#ifndef WIN32
    if ((ret = connect()) != ERROR_SUCCESS) {
        srs_warn("http connect server failed. ret=%d", ret);
        return ret;
    }

    // send POST request to uri
    // GET %s HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s
    std::stringstream ss;
    ss << "GET " << path << " "
       << "HTTP/1.1" << SRS_HTTP_CRLF
       << "Host: " << host << SRS_HTTP_CRLF
       << "Connection: Keep-Alive" << SRS_HTTP_CRLF
       << "Content-Length: " << std::dec << req.length() << SRS_HTTP_CRLF
       << "User-Agent: " << RTMP_SIG_SRS_NAME << RTMP_SIG_SRS_VERSION << SRS_HTTP_CRLF
       << "Content-Type: application/json" << SRS_HTTP_CRLF
       << SRS_HTTP_CRLF
       << req;

    std::string data = ss.str();
    if ((ret = skt->write((void*)data.c_str(), data.length(), NULL)) != ERROR_SUCCESS) {
        // disconnect when error.
        disconnect();
        srs_error("write http get failed. ret=%d", ret);
        return ret;
    }

    ISrsHttpMessage* msg = NULL;
    if ((ret = parser->parse_message(skt, NULL, &msg)) != ERROR_SUCCESS) {
        srs_error("parse http post response failed. ret=%d", ret);
        return ret;
    }
#else
    std::string resp;
    bool done = false;

    std::thread thd([this, &path, &done, &resp] {
        libcurlget("http://" + host + ":" + std::to_string(port) + path, resp);
        done = true;
    });
    thd.detach();

    while (done == false) {
        st_usleep(0);
    }

    auto msg = new SrsHttpMessage(nullptr, nullptr);
    if (resp.empty() == true) {
        msg->status_code(SRS_CONSTS_HTTP_InternalServerError);
    } else {
        msg->status_code(SRS_CONSTS_HTTP_OK);
        msg->body_write_all(resp);
    }

#endif
    if (msg != nullptr) {
        *ppmsg = msg;
    }
    srs_info("parse http get response success.");
    return ret;
}

void SrsHttpClient::disconnect() {
#ifndef WIN32
    connected = false;
    srs_close_stfd(stfd);
    srs_freep(skt);
#endif
}

int SrsHttpClient::connect() {
    int ret = ERROR_SUCCESS;

#ifndef WIN32
    if (connected) {
        return ret;
    }
    disconnect();

    // open socket.
    if ((ret = srs_socket_connect(host, port, timeout_us, &stfd)) != ERROR_SUCCESS) {
        srs_warn("http client failed, server=%s, port=%d, timeout=%" PRId64 ", ret=%d",
                 host.c_str(), port, timeout_us, ret);
        return ret;
    }
    srs_info("connect to server success. server=%s, port=%d", host.c_str(), port);

    srs_freep(skt);
    skt = new SrsStSocket(stfd);
    connected = true;

    // set the recv/send timeout in us.
    skt->set_recv_timeout(timeout_us);
    skt->set_send_timeout(timeout_us);
#endif
    return ret;
}

#endif
